/*
 *
 * (c) Copyright Ascensio System Limited 2010-2018
 *
 * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU 
 * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). 
 * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that 
 * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
 *
 * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
 *
 * You can contact Ascensio System SIA by email at sales@onlyoffice.com
 *
 * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display 
 * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
 *
 * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains 
 * relevant author attributions when distributing the software. If the display of the logo in its graphic 
 * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" 
 * in every copy of the program you distribute. 
 * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
 *
*/


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;

using ASC.Common.Logging;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using Formatting = Newtonsoft.Json.Formatting;

namespace ASC.Resource.Manager
{
    public static class JsonManager
    {
        public static void Upload(IOptionsMonitor<ILog> option, ResourceData resourceData, string fileName, Stream fileStream, string projectName, string moduleName)
        {
            var culture = GetCultureFromFileName(fileName);

            string jsonString;
            using (var reader = new StreamReader(fileStream))
            {
                jsonString = reader.ReadToEnd();
            }

            var jsonObj = new Dictionary<string, string>();

            if (Path.GetExtension(fileName) == ".xml")
            {
                var doc = new XmlDocument();
                doc.LoadXml(jsonString);
                var list = doc.SelectNodes("//resources//string");
                if (list != null)
                {
                    try
                    {
                        var nodes = list.Cast<XmlNode>().ToList();
                        jsonObj = nodes.ToDictionary(r => r.Attributes["name"].Value, r => r.InnerText);
                    }
                    catch (Exception e)
                    {
                        option.CurrentValue.ErrorFormat("parse xml " + fileName, e);
                    }
                }
            }
            else
            {
                jsonObj = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);
            }

            var fileID = resourceData.AddFile(fileName, projectName, moduleName);
            const string resourceType = "text";
            foreach (var key in jsonObj.Keys)
            {
                var word = new ResWord
                {
                    Title = key,
                    ValueFrom = jsonObj[key],
                    ResFile = new ResFile { FileID = fileID }
                };
                if (culture != "Neutral")
                {
                    var neutralKey = new ResWord
                    {
                        Title = key,
                        ValueFrom = jsonObj[key],
                        ResFile = new ResFile { FileID = fileID }
                    };

                    resourceData.GetValueByKey(neutralKey, "Neutral");
                    if (string.IsNullOrEmpty(neutralKey.ValueTo)) continue;
                }

                resourceData.AddResource(culture, resourceType, DateTime.UtcNow, word, true, "Console");
            }
        }

        public static void Export(IServiceProvider serviceProvider, string project, string module, string fName, string language, string exportPath, string key = null)
        {
            var filter = new ResCurrent
            {
                Project = new ResProject { Name = project },
                Module = new ResModule { Name = module },
                Language = new ResCulture { Title = language },
                Word = new ResWord { ResFile = new ResFile { FileName = fName } }
            };

            using var scope = serviceProvider.CreateScope();
            var resourceData = scope.ServiceProvider.GetService<ResourceData>();
            var words = resourceData.GetListResWords(filter, string.Empty).GroupBy(x => x.ResFile.FileID).ToList();

            if (!words.Any())
            {
                Console.WriteLine("Error!!! Can't find appropriate project and module. Possibly wrong names!");
                return;
            }

            foreach (var fileWords in words)
            {
                var wordsDictionary = new Dictionary<string, object>();
                var firstWord = fileWords.FirstOrDefault();
                var fileName = firstWord == null
                    ? module
                    : Path.GetFileNameWithoutExtension(firstWord.ResFile.FileName);
                var zipFileName = Path.Combine(exportPath, language == "Neutral" ? "en" : language, $"{fileName}.json");

                var dirName = Path.GetDirectoryName(zipFileName);
                if (!Directory.Exists(dirName))
                {
                    Directory.CreateDirectory(dirName);
                }

                var toAdd = new List<ResWord>();
                if (!string.IsNullOrEmpty(key))
                {
                    if (File.Exists(zipFileName))
                    {
                        var jObject = JObject.Parse(File.ReadAllText(zipFileName));
                        foreach (var j in jObject)
                        {
                            toAdd.Add(new ResWord { Title = j.Key, ValueFrom = j.Value.ToString() });
                        }
                    }

                    if (!toAdd.Any(r => r.Title == key))
                    {
                        toAdd.Add(fileWords.FirstOrDefault(r => r.Title == key));
                    }
                }
                else
                {
                    toAdd.AddRange(fileWords.OrderBy(x => x.Title).Where(word => !wordsDictionary.ContainsKey(word.Title)));
                }

                foreach (var word in toAdd.Where(r => r != null))
                {
                    if (string.IsNullOrEmpty(word.ValueTo)) continue;

                    var newVal = word.ValueTo ?? word.ValueFrom;

                    if (!string.IsNullOrEmpty(newVal))
                    {
                        newVal = newVal.TrimEnd('\n').TrimEnd('\r');
                    }

                    var newKey = GetKey(word.Title, newVal);
                    wordsDictionary.Add(newKey.Keys.First(), newKey.Values.First());
                }

                using TextWriter writer = new StreamWriter(zipFileName);

                var obj = JsonConvert.SerializeObject(wordsDictionary, Formatting.Indented);
                writer.Write(obj);

            }
        }

        private static string GetCultureFromFileName(string fileName)
        {
            var culture = "Neutral";
            var nameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);

            if (nameWithoutExtension != null && nameWithoutExtension.Split('.').Length > 1)
            {
                culture = nameWithoutExtension.Split('.')[1];
            }

            return culture;
        }

        private static Dictionary<string, object> GetKey(string title, string val)
        {
            var splited = title.Split('.');
            if (splited.Length == 1) return new Dictionary<string, object>() { { title, val } };

            return new Dictionary<string, object>() { { splited[0], GetKey(title.Substring(title.IndexOf('.') + 1), val) } };
        }
    }
}